import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { map, catchError } from 'rxjs/operators';
import 'rxjs/add/observable/throw';

import Polygon from 'ol/geom/polygon';
import * as coordtransform from 'coordtransform';

import { coordinateTransform } from '../util';
import { PlaceTextOption, Poi, DistrictOptions, District, UserLocation } from '../models';

@Injectable()
export class RestapiAmapService {

  private key: string = '2daa8d177fef55674e9231a423cc2822';

  constructor(
    private http: HttpClient
  ) { }

  private buildUrl(path: string, data: any): string {
    let url = path + '?key=' + this.key;
    for(let key in data) {
      url += '&' + key + '=' + data[key];
    }
    return encodeURI(url);
  }

  placeText(options: PlaceTextOption): Observable<Poi[]> {
    const url: string = this.buildUrl('http://restapi.amap.com/v3/place/text', options);
    return this.http.get<Poi[]>(url)
      .pipe(
        map((data: any) => {
          const pois = data.pois;
          let result: Poi[] = [];
          if(pois) {
            result = pois.map((poi: any) => {
              let coordinate = poi.location.split(',');
              coordinate[0] = parseFloat(coordinate[0]);
              coordinate[1] = parseFloat(coordinate[1]);
              return {
                id: poi.id,
                address: poi.name,
                district: `${poi.cityname}${poi.adname}`,
                point: coordinateTransform(coordinate, 'gcj02', 'EPSG:3857')
              }
            });
          }
          return result
        }),
        catchError((error: any) => Observable.throw(error))
      );
  }

  district(options: DistrictOptions): Observable<District[]> {
    const url: string = this.buildUrl(`http://restapi.amap.com/v3/config/district`, options);
    return this.http.get<District[]>(url)
      .pipe(
        map((data: any) => {
          let districts = [];
          if(data.districts && data.districts[0]) {
            districts = data.districts[0].districts || [];
          }
          let result: District[] = this.formatDistrict(districts);
          result.sort((a, b) => parseFloat(a.adcode) - parseFloat(b.adcode));
          return result;
        }),
        catchError((error: any) => Observable.throw(error))
      );
  }

  districtByAdcode(adcode: string): Observable<UserLocation> {
    const options: DistrictOptions = {
      subdistrict: 0,
      extensions: 'all',
      keywords: adcode
    };
    const url: string = this.buildUrl(`http://restapi.amap.com/v3/config/district`, options);
    return this.http.get<District[]>(url)
      .pipe(
        map((data: any) => {
          let district = null;
          if(data.districts && data.districts[0]) {
            district = data.districts[0];
          }
          return {
            adcode: district.adcode,
            city: this.alias(district.name),
            extent: this.tranformPolylineToExtent(district.polyline)
          }
        }),
        catchError((error: any) => Observable.throw(error))
      );
  }

  getLoaction(): Observable<UserLocation> {
    const url: string = this.buildUrl(`http://restapi.amap.com/v3/ip`, {});
    return this.http.get(url)
      .pipe(
        map((data: any) => {
          return {
            city: this.alias(data.city),
            adcode: data.adcode,
            extent: this.tranformPolylineToExtent(data.rectangle),
          };
        }),
        catchError((error: any) => Observable.throw(error))
      );
  }

  private tranformPolylineToExtent(polyline: string = ''): number[] {
    let coordinates = this.translatePolyline(polyline);
    let geometry = new Polygon(coordinates);
    geometry.transform('EPSG:4326', 'EPSG:3857');
    return  geometry.getExtent();
  }

  private formatDistrict(districts: any[]): District[] {
    let result: District[] = districts.map((district: any) => {
      return {
        citycode: district.citycode,
        adcode: district.adcode,
        name: this.alias(district.name),
        districts: district.districts ? this.formatDistrict(district.districts) : []
      }
    });
    return result;
  }

  private alias(name: string): string {
    let regs = [
      /特别行政区$/g,
      /自治区$/g,
      /省$/g,
      /市$/g,
      /县$/g,
      /回族$/g,
      /维吾尔$/g
    ], key, reg, ret = name;
    if(name === '中华人民共和国') {
      ret = '全国';
    } else {
      for(key in regs) {
        reg = regs[key];
        ret = ret.replace(reg, '');
      }
    }
    return ret;
  }

  private translatePolyline(polyline) {
    let polylines = polyline.split('|'), key, ret = [];
    for(key in polylines) {
      let line = polylines[key];
      ret.push(this.stringToPoints(line));
    }
    return ret;
  }

  private  stringToPoints(line: string) {
    let linePoints = line.split(';'),
        key,
        point,
        ret = [];
    for(key in linePoints) {
      point = this.gcj02towgs84(linePoints[key]);
      ret.push([point.lng, point.lat]);
    }
    return ret;
  };

  private gcj02towgs84(location: string, array: boolean = false): any {
    let gcj02Loc = location.split(',');
    let wgs84Loc = coordtransform.gcj02towgs84(gcj02Loc[0], gcj02Loc[1]);
    if(array) {
      return wgs84Loc;
    } else {
      return { lat: wgs84Loc[1], lng: wgs84Loc[0] }
    }
  }

}
